﻿#ifndef ENGINE_H
#define ENGINE_H

#include <string>
#include <map>
//#include "vector<Move>.h"
#include "Watch.h"
#include "ReturnCode.h"
#include "BrainMode.h"
#include "BrainStatus.h"
#include "Position.h"
#include "Types.h"
#include "EngineOption.h"

// to test the different implementations
enum SearchImplementation {
	SEARCH_IMPL_ALPHABETA,
	SEARCH_IMPL_NEGMAX,
	SEARCH_IMPL_ITERATIVE_AB,
	SEARCH_IMPL_NEGASCOUT
};

class Engine {

//private:
public:
	//Thread mainThread;
	//Thread clockThread;
	bool debugMode;


public: 
	SearchImplementation searchImplementation;
	//bool debugNegmax;
	//bool debugIterativeAB;
	//bool debugQuiesce;
	//bool debugNegascout;

//private:
	Position currentPosition;
	Color GetTurn(); // inline = currentPosition.gameState.turn
public:
	Watch watch;

	long nodeMaxNumber;
	long nbProcessedNodes;
	int selectiveDepth;
public:
	BrainMode brainMode;
	BrainStatus brainStatus;
	int depthLevel;
	long thinkDelay;

	//TODO: replace by time[Color][] and inc[Color][]
	long wtime; //total time left for white in millisec
	long btime; //total time left for black in millisec
	long winc;
	long binc;

	bool isVerbose;

	bool isSmartTiming;
	bool isSearchMove;

	// New Alpha-beta implementation
	vector<Move> moves;// = new vector<Move>();
	//vector<MoveStack> scores;// = new vector<MoveStack>();
	vector<MoveScorePv> scores;// = new vector<MoveStack>();

//private:
	Move bestMove;
	int bestScore;
	Move* bestPv;
	//private int maxTTsize;

public:
	Engine();
	void init();

	map<string, EngineOption> option;

	void Play(Move m);
	//void ClockThreadLoop();
	//void BrainMainThreadLoop();
	void Quit();
	ReturnCode Search(Position& position, int depth);
	ReturnCode AlphaBetaMinMax(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth, bool maxPlayer);
	
	void DisplayScores(vector<MoveStack>& scores, int depth);
	void DisplayScores(vector<MoveScorePv>& scores, int depth);

	void Sort(vector<MoveStack>& scores);
	void Sort(Board & b, vector<Move> & moves);
	void Sort(vector<MoveScorePv> & scores);
	
	//ReturnCode AlphaBetaMax(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth);
	//ReturnCode AlphaBetaMin(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth);
	ReturnCode AlphaBetaMax(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth, Move* pv); //version with best pv
	ReturnCode AlphaBetaMin(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth, Move* pv); //version with best pv
	ReturnCode AlphaBetaNegmaxMin(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth);
	ReturnCode NegaScout(int& score, Position& pos, int alpha, int beta, int depth, int maxDepth);
	ReturnCode AlphaBetaIterative(int& score, Position& position, int alpha, int beta, int depth, int maxDepth);
	void Stop();
	void Start();

	void DisplayMoves(vector<Move>& moves);
	void SetDebug(bool isDebugOn);
	void SetOption(string name, string value);
	void SetSmartTiming(bool value);
	int EvaluateRelative(Position& position, Color turn);
	int EvaluateAbsolute(Position& position);
	//float AttackFactor(Position& position);

	
	bool IsCheck(Position& position);
	//bool IsCheck(Position& position, Color bySide);

	bool IsCheckmate(Position& position);
	//bool IsThreeTimesRepetitionDraw(Position& pos);
	//bool IsCheck(Position& position, bool playerColor);
	void GetPossibleMoves(Position& position, vector<Move>& moves);
	void GetPossibleMoves(Position& position, Square square, vector<Move> & possibleMoves);
	void GetPossibleKingMoves(Position& position, Square square, vector<Move> & possibleKingMoves);
	void GetPossibleQueenMoves(Position& position, Square square, vector<Move> & possibleQueenMoves);
	void GetPossibleRookMoves(Position& position, Square square, vector<Move> & possibleRookMoves);
	void GetPossibleKnightMoves(Position& position, Square square, vector<Move> & possibleKnightMoves);
	void GetPossibleBishopMoves(Position& position, Square square, vector<Move> & possibleBishopMoves);
	void GetPossiblePawnMoves(Position& position, Square square, vector<Move> & possiblePawnMoves);
	int GetLegalMoves(Position& pos, vector<Move> & legalMoves);

	// for selective search
	void GetPossibleCaptures(Position& position, vector<Move>& moves);
	void GetPossibleCaptures(Position& position, Square square, vector<Move> & possibleMoves);
	void GetPossibleKingCaptures(Position& position, Square square, vector<Move> & possibleKingMoves);
	void GetPossibleQueenCaptures(Position& position, Square square, vector<Move> & possibleQueenMoves);
	void GetPossibleRookCaptures(Position& position, Square square, vector<Move> & possibleRookMoves);
	void GetPossibleKnightCaptures(Position& position, Square square, vector<Move> & possibleKnightMoves);
	void GetPossibleBishopCaptures(Position& position, Square square, vector<Move> & possibleBishopMoves);
	void GetPossiblePawnCaptures(Position& position, Square square, vector<Move> & possiblePawnMoves);
	int GetLegalCaptures(Position& pos, vector<Move> & legalMoves);

	
	bool IsFiftyMovesDraw(Position& pos);
	Move GetBestMove();
	void SetDepthLevel(int depth);
	void SetTimeLevel(int timeLevel);
	void SetStartPosition();
	void SetPosition(string strPos);
	void Ponderhit();
	void SetSearchmovesMode(vector<Move>& selectedMoves);
	void SetPonderMode();
	void SetWtime(int timeLeft);
	void SetBtime(int timeLeft);
	void SetWinc(int increment);
	void SetBinc(int increment);
	void SetMovestogo(int movesUntilNextTimeControl);
	void SetDepthMode(int pliesDepth);
	void SetNodesMode(int nodesDepth);
	void SetMateMode(int mateDepth);
	void SetMovetimeMode(int searchTime);
	void SetInfiniteMode();
	void SetVerboseMode(bool isVerboseMode);
	void UpdateThinkDelay();
	void SetPosition(Position& pos);
	string ScoreToString(int score);
	Position& GetCurrentPosition();
	int GetBestScore();

	void DisplayBestPv(int maxDepth, long nodeNb, long time, Move* pv);
	void DisplayPv(Move* pv);

public:

	string GetEngineInfo();
	string GetEngineOptions();

//private:
	int potential_value(Board & b, Move m);
	//bool compare_potential_value(MoveStack m1, MoveStack m2);
};


inline void Engine::SetSmartTiming(bool value)
{
	isSmartTiming = value;
}



inline int Engine::EvaluateRelative(Position& position, Color turn)
{
	// TODO: improve the evaluation method
	// SIDE RELATIVE EVAL FUNCTION
	//materialScore = kingWt  * (wK-bK)
	//              + queenWt * (wQ-bQ)
	//              + rookWt  * (wR-bR)
	//              + knightWt* (wN-bN)
	//              + bishopWt* (wB-bB)
	//              + pawnWt  * (wP-bP)
	//mobilityScore = mobilityWt * (wMobility-bMobility)
	//Eval  = (materialScore + mobilityScore) * who2Move


	// Part 1 - Basic evaluation : just count the pieces for ech side with coefs
	int staticEval = 0; 
	//if (turn==WHITE) 
	//	staticEval = position.Evaluate();
	//else 
	//	staticEval = -position.Evaluate();

	staticEval = position.Evaluate(turn);

	// Part 2 - Attack factor = how many captures are possible from this position
	// max attack factor 
	/*
	int attackMaxValue = turn ? W_coeff[0] : B_coeff[0];
	int attackOffset = (int)(attackMaxValue*AttackFactor(position));

	// Part 3 - Positionnal score : the delta score which can make
	//          the difference between to positions with the same score
	//          Note: this delta-score should be a [-100cp;100cp] value maximum
	// TODO: implement this part with the use of Bitboards
	*/
	return staticEval /*+ attackOffset*/;
}

inline int Engine::EvaluateAbsolute(Position& position)
{
	// TODO: improve the evaluation method with bitboards
	// SHANNON'S EVAL FUNCTION
	//f(p) = 200(K-K')
	//       + 9(Q-Q')
	//       + 5(R-R')
	//       + 3(B-B' + N-N')
	//       + 1(P-P')
	//       - 0.5(D-D' + S-S' + I-I')
	//       + 0.1(M-M') + ...
	//KQRBNP = number of kings, queens, rooks, bishops, knights and pawns
	//D,S,I = doubled, blocked and isolated pawns
	//M = Mobility (the number of legal moves)
	
	return position.Evaluate();
}

inline bool Engine::IsFiftyMovesDraw(Position& pos)
{
	return (pos.gameState.halfMoveClock >= 50);
}

inline Color Engine::GetTurn(){
	return currentPosition.gameState.turn;
}

extern Engine ChessEngine; 

//extern long instanceCounter;  // for perf debugging

#endif